#include "test_byte.h"
#include "string.h"
#include "stdlib.h"

#include "esp_timer.h"
#include "esp_log.h"
#include "esp_sleep.h"

#include "driver/gpio.h"

#include "lua_user_model.h"

#include "mqtt_client.h"

#define MODEL_NAME "mqtt"
//测试模板
typedef struct {
    lua_State *L;//lua对象
    int funcIndex;//回调函数
    esp_mqtt_client_handle_t client;
}user_mqtt;


static void mqtt_event_handler(void *handler_args, esp_event_base_t base, int32_t event_id, void *event_data){
    user_mqtt*mqtt=(user_mqtt*)handler_args;
    lua_State *L=mqtt->L;

    esp_mqtt_event_handle_t event = event_data;
    //esp_mqtt_client_handle_t client = event->client;

    char*topic=NULL;

    if(event->topic_len>0){
        topic=(char*)malloc(event->topic_len+1);
        memcpy(topic,event->topic,event->topic_len);
        topic[event->topic_len]=0;
    }

    lua_rawgeti(L, LUA_REGISTRYINDEX, mqtt->funcIndex);
    lua_pushinteger(L,event_id);
    lua_pushstring(L, topic);
    user_byte*bf=(user_byte*)malloc(sizeof(user_byte));
    user_byte**temp = (user_byte**)lua_newuserdata(L, sizeof(user_byte*));
    *temp=bf;
    char*data=NULL;
    if(event->data_len>0){
        data=(char*)malloc(event->data_len+1);
        memcpy(data,event->data,event->data_len);
        data[event->data_len]=0;
        bf->buff=data;
    }
    bf->size=event->data_len;
    luaL_getmetatable(L, "byte");
    lua_setmetatable(L,-2);
    int r=lua_pcall(L, 3, 0,0);
    if(r>0){
        const char*msg=luaL_checkstring(L,-1);
        printf("err:%s\r\n",msg);
    }
    *temp=NULL;
    free(bf);
    if(topic!=NULL){
        free(topic);
    }
    if(data!=NULL){
        free(data);
    }
}

//取消订阅消息
static int lua_user_unsubscribe(lua_State *L){
    user_mqtt**mqtt=(user_mqtt**)lua_touserdata(L,1);
    char*topic=(char*)luaL_checkstring(L,2);
    int msg_id = esp_mqtt_client_unsubscribe((*mqtt)->client, topic);
    lua_pushinteger(L,msg_id);
    return 1;
}

//订阅消息
static int lua_user_subscribe(lua_State *L){
    int top=lua_gettop(L);
    user_mqtt**mqtt=(user_mqtt**)lua_touserdata(L,1);
    char*topic=(char*)luaL_checkstring(L,2);
    int qos=0;
    if(top>=3){
        qos=luaL_checkinteger(L,3);
    }

    int msg_id = esp_mqtt_client_subscribe((*mqtt)->client, topic, qos);
    lua_pushinteger(L,msg_id);
    return 1;
}

// const char *topic,const char *data, int qos, int retain
static int lua_user_publish(lua_State *L){
     user_mqtt**mqtt=(user_mqtt**)lua_touserdata(L,1);
     int top=lua_gettop(L);
     const char*topic=luaL_checkstring(L,2);
     int len=0,qos=1,retain=0;
     char*data=NULL;
     int type=lua_type(L,3);
     if(type==LUA_TSTRING){
        data=(char*)luaL_checkstring(L,3);
     }else{
        user_byte**temp=(user_byte**)lua_touserdata(L,3);
        data=(char*)(*temp)->buff;
        len=(*temp)->size;
     }

     if(top==4){
        qos=luaL_checkinteger(L,4);
     }else if(top==5){
        qos=luaL_checkinteger(L,4);
        retain=luaL_checkinteger(L,5);
     }
    int msg_id = esp_mqtt_client_publish((*mqtt)->client, topic, data, len,qos,retain );

    lua_pushinteger(L,msg_id);
     return 1;
}


//设置回调函数
static int lua_user_callback(lua_State *L){
    user_mqtt**mqtt=(user_mqtt**)lua_touserdata(L,1);
    int funcIndex = luaL_ref(L, LUA_REGISTRYINDEX);
    (*mqtt)->funcIndex=funcIndex;
    esp_mqtt_client_register_event((*mqtt)->client, ESP_EVENT_ANY_ID, mqtt_event_handler, *mqtt);
    return 0;
}

// 
static int lua_user_connect(lua_State *L){
    user_mqtt**mqtt=(user_mqtt**)lua_touserdata(L,1);
    int err=esp_mqtt_client_start((*mqtt)->client);
    lua_pushinteger(L,err);
    return 1;
}

// tostring 方法 
static int lua_user_tostring(lua_State *L){
    lua_pushstring(L,"lua socket model");
    return 1;
}

// 执行 collectgarbage()函数会进行gc
static int lua_user_gc(lua_State *L){
    //user_template**timer=(user_template**)lua_touserdata(L,1);
    return 0;
}

static const luaL_Reg lua_user_func[] ={ 
    {"callback", lua_user_callback}, 
    {"connect", lua_user_connect}, 
    {"publish", lua_user_publish},
    {"subscribe", lua_user_subscribe},
    {"unsubscribe",lua_user_unsubscribe},
    {"pub", lua_user_publish},
    {"sub", lua_user_subscribe},
    {"unsub",lua_user_unsubscribe},
    {"__tostring", lua_user_tostring}, 
    {"__gc", lua_user_gc}, 
	{NULL, NULL}
};

// cleintid,url,userName,password;
// mqtt.create('clinet',22,call)
static int lua_create_new(lua_State *L){
     //创建一个对象指针放到stack里，返回给Lua中使用，userdata的位置-1
    user_mqtt*mqtt=(user_mqtt*)malloc(sizeof(user_mqtt));
    mqtt->L=L;
    esp_mqtt_client_config_t mqtt_cfg={};
    //cleintid,url,userName,password;
    int top=lua_gettop(L);
    for(int i=1;i<top+1;i++){
        switch (i){
            case 1:
              const char*clientId=luaL_checkstring(L,i);
              mqtt_cfg.credentials.client_id=clientId;
              break;
            case 2:
              const char*url=luaL_checkstring(L,i);
              mqtt_cfg.broker.address.uri=url;
              break;
            case 3:
              const char*userName=luaL_checkstring(L,i);
              mqtt_cfg.credentials.username=userName;
              break;
            case 4:
              const char*password=luaL_checkstring(L,i);
              mqtt_cfg.credentials.authentication.password=password;
              break;
            default:

        }
    }

    esp_mqtt_client_handle_t client = esp_mqtt_client_init(&mqtt_cfg);
    mqtt->client=client;
    user_mqtt**temp = (user_mqtt**)lua_newuserdata(L, sizeof(user_mqtt*));
    *temp=mqtt;
    //Lua->stack，得到全局元表位置-1,userdata位置-2
    luaL_getmetatable(L, MODEL_NAME);
    //将元表赋值给位置-2的userdata，并弹出-1的元表
    lua_setmetatable(L, -2);
    return 1;
}

static const luaL_Reg lua_user_constructor_funcs[] = {
    { "create", lua_create_new },
    { NULL, NULL }
};

int luaopen_user_mqtt_libs (lua_State *L) {
     //创建全局元表（里面包含了对LUA_REGISTRYINDEX的操作），元表的位置为-1
    luaL_newmetatable(L, MODEL_NAME);

    //将元表作为一个副本压栈到位置-1，原元表位置-2
    lua_pushvalue(L, -1);

    //设置-2位置元表的__index索引的值为位置-1的元表，并弹出位置-1的元表，原元表的位置为-1
    lua_setfield(L, -2, "__index");

    //将成员函数注册到元表中（到这里，全局元表的设置就全部完成了）
    luaL_setfuncs(L, lua_user_func, 0);
    //注册构造函数到新表中，并返回给Lua
    luaL_newlib(L, lua_user_constructor_funcs);
    return 1;
}